package devices

import (
	"bytes"
	"fmt"

	"os"
	"path"
	"strings"
)

// IGPIODevice GPIO 设备操作接口
// /sys/class/gpio/gpio141
// active_low device direction edge power subsystem uevent value
type IGPIODevice interface {
	GetPath() string
	GetValuePath() string
	GetDirectionPath() string
	GetDevicePath() string

	Export() (bool, error)
	UnExport() (bool, error)

	IsExported() bool
	IsUnExported() bool

	Value() (GPIOValue, error)
	ChangeValue(value GPIOValue) error

	Mode() (GPIOMode, error)
	ChangeMode(mode GPIOMode) error

	Edge() (GPIOEdge, error)
	ChangeEdge(edge GPIOEdge) error
}

// GPIODevice 设备信息
type GPIODevice struct {
	Id   int    // GPIO 编号
	Path string // GPIO 设备目录
}

// NewGPIODevice 实例化一个设备
func NewGPIODevice(id int) *GPIODevice {
	return &GPIODevice{
		Id:   id,
		Path: path.Join(GPIO, fmt.Sprintf("gpio%d", id)),
	}
}

// isSetPath 内置:是否已经设置了路径
func (device *GPIODevice) isSetPath() bool {
	if len(device.Path) > 0 {
		return strings.Contains(device.Path, GPIO)
	}
	return false
}

// GetPath 获取设备目录路径
func (device *GPIODevice) GetPath() string {
	if device.isSetPath() {
		return device.Path
	}
	return path.Join(GPIO, fmt.Sprintf("gpio%d", device.Id))
}

// GetValuePath 获取GPIO设备的 value 路径
func (device *GPIODevice) GetValuePath() string {
	return path.Join(device.GetPath(), "value")
}

// GetDirectionPath 获取GPIO设备的 direction 路径
func (device *GPIODevice) GetDirectionPath() string {
	return path.Join(device.GetPath(), "direction")
}

// GetDirectionPath 获取GPIO设备的 direction 路径
func (device *GPIODevice) GetEdgePath() string {
	return path.Join(device.GetPath(), "edge")
}

// GetDevicePath 获取GPIO设备的 device 路径
func (device *GPIODevice) GetDevicePath() string {
	return path.Join(device.GetPath(), "device")
}

// IsExported 检查是否已经导出设备引脚
func (device *GPIODevice) IsExported() bool {
	_, err := os.Stat(device.GetPath())
	return err == nil
}

// IsUnExported 检查是否已经导出设备引脚
func (device *GPIODevice) IsUnExported() bool {
	return !device.IsExported()
}

// Export 导出设备
func (device *GPIODevice) Export() (bool, error) {
	if device.IsExported() {
		return true, nil
	}

	err := GPIOExport(device.Id)
	return err == nil, err
}

// UnExport 取消导出设备
func (device *GPIODevice) UnExport() (bool, error) {
	if device.IsExported() {
		err := GPIOUnExport(device.Id)
		return err == nil, err
	}
	return true, nil
}

// 获取
func (device *GPIODevice) Value() (value GPIOValue, err error) {
	value = LEVEL_LOW // 初始化默认值

	content, err := os.ReadFile(device.GetDirectionPath())
	if err != nil {
		return
	}
	if string(content) == "0" {
		value = LEVEL_LOW
	} else {
		value = LEVEL_HIGH
	}
	return
}

// ChangeValue 改变value值
func (device *GPIODevice) ChangeValue(value GPIOValue) (err error) {
	if value == LEVEL_LOW {
		err = os.WriteFile(device.GetValuePath(), []byte("0"), os.ModePerm)
	}
	if value == LEVEL_HIGH {
		err = os.WriteFile(device.GetValuePath(), []byte("1"), os.ModePerm)
	}
	return
}

// Mode 获取当前工作模式
func (device *GPIODevice) Mode() (mode GPIOMode, err error) {
	mode = DIRECTION_IN

	content, err := os.ReadFile(device.GetDirectionPath())
	if err != nil {
		return
	}

	if bytes.Contains(content, []byte("in")) {
		mode = DIRECTION_IN
	} else {
		mode = DIRECTION_OUT
	}
	return
}

// ChangeMode 改变工作模式
func (device *GPIODevice) ChangeMode(mode GPIOMode) (err error) {
	if mode == DIRECTION_IN {
		err = os.WriteFile(device.GetDirectionPath(), []byte("in"), os.ModePerm)
	} else {
		err = os.WriteFile(device.GetDirectionPath(), []byte("out"), os.ModePerm)
	}
	return
}

// Edge 获取当前触发模式
func (device *GPIODevice) Edge() (edge GPIOEdge, err error) {
	edge = EDGE_NONE

	content, err := os.ReadFile(device.GetEdgePath())
	if err != nil {
		return
	}

	if bytes.Contains(content, []byte("none")) {
		edge = EDGE_NONE
	}
	if bytes.Contains(content, []byte("rising")) {
		edge = EDGE_RISING
	}
	if bytes.Contains(content, []byte("none")) {
		edge = EDGE_FALLING
	}
	if bytes.Contains(content, []byte("both")) {
		edge = EDGE_BOTH
	}
	return
}

// ChangeEdge 改变触发模式
func (device *GPIODevice) ChangeEdge(edge GPIOEdge) (err error) {

	var changed bool = false
	for _, v := range EDGE_VALUES {
		if edge == v {
			err = os.WriteFile(device.GetEdgePath(), []byte(v), os.ModePerm)

			if err == nil {
				changed = true
			}
		}
	}

	if !changed {
		err = fmt.Errorf("ChangeEdge: not change gpio%d edge '%s'", device.Id, edge)
	}

	return
}
